/*
 * Creado por NIN
 * User: sebastian.lucas
 * Date: 30/04/2007
 * Time: 15:24
 */

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

#if DEBUG
    using System.Diagnostics;
#endif


namespace CSE
{
    /*
	 * TimeLineControl
	 */
	public partial class TimeLine : UserControl
	{
	    /*
	     * Constants
	     */
	    public static readonly Color color_keyframe_empty_a = Color.FromArgb(255,255,255,255);
	    public static readonly Color color_keyframe_empty_b = Color.FromArgb(255,193,204,216);
	    public static readonly Color color_keyframe_sep     = Color.FromArgb(255,131,153,177);
	    public static readonly Color color_keyframe_fill    = Color.FromArgb(255,104,104,104);
	    
	    public static readonly Color color_timeline_bkg = Color.FromArgb(255,193,204,216);
	    public static readonly Color color_timeline_fkg = Color.FromArgb(255,79,101,125);
	    
	    public static readonly int MAX_LAYERS       = 99;
	    public static readonly int MAX_KEYFRAMES    = 99;
	    public static readonly int LAYER_HEIGHT     = 18;
	    
	    public static readonly int MIDDLE_PERCENT   = 30;

	    public static readonly int KEYFRAME_WIDTH   = 7;
	    public static readonly int KEYFRAME_HEIGHT  = LAYER_HEIGHT;
	    public static readonly int KEYFRAME_SPACE   = 4;
	    public static readonly int KEYFRAME_SIMPLE_A    = 0;
	    public static readonly int KEYFRAME_SIMPLE_B    = 1;
	    public static readonly int KEYFRAME_FILL        = 2;
	    public static readonly int KEYFRAME_START       = 3;
	    public static readonly int KEYFRAME_END         = 4;
	    public static readonly int KEYFRAME_BETWEEN     = 5;
	    
	    public static readonly int LAYER_DELETE         = 0;
	    
	    public static readonly int KEYFRAME_ADD			= 100;
	    
	    public static readonly int TYPE_EMPTY = -1;
	    public static readonly int TYPE_START = KEYFRAME_START;
	    public static readonly int TYPE_END = KEYFRAME_END;
	    public static readonly int TYPE_BETWEEN = KEYFRAME_BETWEEN;
	    
	    /*
	     * Variables
	     */
	    private int w = 0;
	    private int h = 0;
	    
	    public int timeline_middle = 0;
	    
	    private int layer_selected = -1;
	    private List<tlLayer> _layersDic = new List<tlLayer>();
	    
	    private int guide_last_index = 0;
	    public int guide_max = MAX_KEYFRAMES;
	    public int guide_index = 0;
	    public int mouse_y = -1;
	    
	    /*
	     *  Events
	     */
	    public delegate void KeyframeChangeHandler(object sender, KeyframeArgs e);
	    public event KeyframeChangeHandler KeyframeChange = null;
	    public delegate void LayerChangeHandler(object sender, LayerArgs e);
	    public event LayerChangeHandler LayerChange = null;
	    public delegate void GuideChangeHandler(object sender, int newpos);
	    public event GuideChangeHandler GuideChange = null;
	    
		/*
		 * Constructor
		 */
	    public TimeLine()
		{
			InitializeComponent();
			KeyframeChange = null;
			
			this.scrLayers.Visible = false;
			this.scrFrames.Visible = false;
		}
	    
	    /*
	     * Clear
	     */
	    public void Clear ()
	    {
	        int i=0;
	        
	        for (i=0; i<_layersDic.Count; i++)
	        {
	            _layersDic[i]._keyframesDic.Clear();
	        }
	        
	        _layersDic.Clear();
	        
	        this.scrLayers.Visible = false;
			this.scrFrames.Visible = false;
			
			this.scrLayers.Value = 0;
			this.scrFrames.Value = 0;
	        
	        layer_selected = -1;
	    }
	    
	    /*
         * AddLayer
         */
        public int AddLayer (string name)
        {
            _layersDic.Add(new tlLayer(name));
            
            if (_layersDic.Count > 0)
            {
                this.scrLayers.Visible = true;
                this.scrLayers.Maximum = _layersDic.Count - 1;
            }
            
            mouse_y = -1;
            this.Invalidate();
            return _layersDic.Count - 1;
        }
        
        /*
         * AddKeyframe
         */
        public int AddKeyframe (int layer, int position, int kindof)
        {
            if (layer < 0
            || layer > _layersDic.Count)
            {
                return -1;
            }
            
            tlLayer l = _layersDic[layer];
            
            l._keyframesDic[position].alt = kindof;
            
            this.Invalidate();
            return l.keyframes_count++;
        }
        
        /*
	     * RecalcTimelineMiddle
	     */
	    private void RecalcTimelineMiddle ()
	    {
	        timeline_middle = (MIDDLE_PERCENT*this.Width)/100;
	        this.scrFrames.Location = new Point(timeline_middle, this.Height - this.scrFrames.Height);
	        this.scrFrames.Width = this.Width - (timeline_middle + this.scrLayers.Width);

	        this.scrLayers.Location = new Point(this.Width - this.scrLayers.Width, 0);
	        this.scrLayers.Height = this.Height - this.scrFrames.Height;
	        
	        w = this.Width - this.scrLayers.Width;
	        h = this.Height - this.scrFrames.Height;
	    }
	    
	    /*
	     * DrawTimeLineBackground
	     */
	    private void DrawTimeLineBackground (Graphics g)
	    {
	        Pen p = new Pen(color_timeline_fkg);
	        SolidBrush b = new SolidBrush(color_timeline_bkg);
	        
	        g.FillRectangle(b, timeline_middle, 0, (w-timeline_middle), h);
	        g.DrawLine(p, timeline_middle - 1, 0, timeline_middle - 1, h);
	        g.DrawLine(p, timeline_middle, 0, w, 0);
	        g.DrawLine(p, timeline_middle, h-1, w, h-1);
	    }
	    
	    /*
	     * DrawTopLineGuide
	     */
	    private void DrawTopLineGuide (Graphics g)
	    {
	        Pen p = new Pen(Color.Red);
	        int x = timeline_middle + (guide_index * KEYFRAME_WIDTH) + guide_index;
	        
	        g.DrawRectangle(p, x, 0, KEYFRAME_WIDTH, KEYFRAME_HEIGHT);
	        g.DrawLine(p, x + (KEYFRAME_WIDTH / 2), KEYFRAME_HEIGHT, x + (KEYFRAME_WIDTH / 2), h);
	        
	        if (GuideChange != null)
			{
            	GuideChange(this, guide_index);
			}
	    }
	    
	    /*
	     * DrawTopLineTime
	     */
	    private void DrawTopLineTime (Graphics g)
	    {
	        int i=0;
	        int x=0;
	        int y=0;
	        int real_guide_index=guide_last_index;
	        Font f = new Font("Sans",8);
	        Pen p = new Pen(Color.Black);
	        SolidBrush b = new SolidBrush(color_keyframe_empty_b);
	        SolidBrush m = new SolidBrush(Color.FromKnownColor(KnownColor.Black));
	        
	        for (i=MAX_KEYFRAMES; i>-1; i--)
	        {
	            x = timeline_middle + (i * KEYFRAME_WIDTH) + i;
	            
	            if (guide_index >= x
	            && guide_index < x + KEYFRAME_WIDTH)
	            {
	                if (i <= guide_max)
	                {
	                    real_guide_index = i;
	                    guide_last_index = i;
	                }
	            }
	            
	            if (x > w)
	            {
	                continue;
	            }
	            
	            g.FillRectangle(b, x, y, KEYFRAME_WIDTH, KEYFRAME_HEIGHT);
	            g.DrawLine(p, x + KEYFRAME_WIDTH, y, x + KEYFRAME_WIDTH, y + 2);
	            
	            if (i % TimeLine.KEYFRAME_SPACE == 0)
	            {
	                g.DrawString((i+1).ToString(), f, m, x - 2, y + 2);
	            }
	        }
	        
	        guide_index = real_guide_index;
	    }
	    
	    /*
	     * DrawSimpleKeyframe
	     */
	    private void DrawSimpleKeyframe (Graphics g, int x, int y, int type)
	    {
	        Pen p = new Pen(color_keyframe_sep);
	        SolidBrush b = null;
	        
	        if (type == KEYFRAME_SIMPLE_A)
	        {
	            b = new SolidBrush(color_keyframe_empty_a);
	        }
	        else if (type == KEYFRAME_SIMPLE_B)
	        {
	            b = new SolidBrush(color_keyframe_empty_b);
	        }
	        else if (type == KEYFRAME_START
	        || type == KEYFRAME_END
	        || type == KEYFRAME_BETWEEN)
	        {
	            b = new SolidBrush(color_keyframe_fill);
	        }
	        
	        g.FillRectangle(b, x, y, KEYFRAME_WIDTH, KEYFRAME_HEIGHT);
	        
	        if (type == KEYFRAME_START
	        && type == KEYFRAME_END
	        && type == KEYFRAME_BETWEEN)
	        {
	            g.DrawLine(p, x + KEYFRAME_WIDTH, y, x + KEYFRAME_WIDTH, y + KEYFRAME_HEIGHT);
	        }
	        
	        if (type == KEYFRAME_START)
	        {
	            b = new SolidBrush(Color.White);
	            g.FillEllipse(b, x-1, y + (KEYFRAME_HEIGHT/3), KEYFRAME_WIDTH, KEYFRAME_WIDTH);
	        }
	        else if (type == KEYFRAME_END)
	        {
	            b = new SolidBrush(Color.White);
	            g.FillRectangle(b, x + 1, y + (KEYFRAME_HEIGHT/3), KEYFRAME_WIDTH - 2, KEYFRAME_WIDTH);
	        }
	        else if (type == KEYFRAME_BETWEEN)
	        {
	            p = new Pen(Color.White);
	            g.DrawLine(p, x, y + (KEYFRAME_HEIGHT / 2), x + KEYFRAME_WIDTH, y + (KEYFRAME_HEIGHT / 2));
	        }
	    }
	    
	    /*
	     * DrawLayer
	     */
	    private void DrawLayer (Graphics g, int index, tlLayer layer)
	    {
	        Pen p = new Pen(color_timeline_fkg);
	        int i = 0;
	        int j = 0;
	        int y = (index * LAYER_HEIGHT) + LAYER_HEIGHT;
	        bool drawbetweenframes = false;
	        
	        for (i=0; i<layer._keyframesDic.Count; i++)
            {
                if (drawbetweenframes
                && layer._keyframesDic[i].alt == KEYFRAME_END)
                {
                    drawbetweenframes = false;
                }
                
                if (!drawbetweenframes)
                {
                    if (layer._keyframesDic[i].alt != -1)
                    {
                        DrawSimpleKeyframe(g, timeline_middle + (i * KEYFRAME_WIDTH) + i, y, layer._keyframesDic[i].alt);
                        
                        if (layer._keyframesDic[i].alt == KEYFRAME_START)
                        {
                            for (j=i+1; j<layer._keyframesDic.Count; j++)
                            {
                                if (layer._keyframesDic[j].alt == KEYFRAME_END)
                                {
                                    drawbetweenframes = true;
                                    break;
                                }
                            }
                        }
                    }
                    else
                    {
                        DrawSimpleKeyframe(g, timeline_middle + (i * KEYFRAME_WIDTH) + i, y, layer._keyframesDic[i].type);
                    }
                }
                else
                {
                    DrawSimpleKeyframe(g, timeline_middle + (i * KEYFRAME_WIDTH) + i, y, KEYFRAME_BETWEEN);
                }
            }
	        
	        if (mouse_y >= y
            && mouse_y < y + LAYER_HEIGHT)
	        {
	            SolidBrush b = new SolidBrush(Color.Black);
	            g.FillRectangle(b, 0, y, timeline_middle, LAYER_HEIGHT);
	            
	            p = new Pen(Color.White);
	            
	            g.DrawLine(p, 0, y, w, y);
	            g.DrawString(layer.name, new Font("Sans",8), new SolidBrush(Color.FromKnownColor(KnownColor.White)), 5, y + 2);
	            g.DrawLine(p, 0, y + LAYER_HEIGHT, w, y + LAYER_HEIGHT);
	            
	            layer_selected = index;
	            this.ContextMenuStrip = this.contextLayer;
	        }
	        else
	        {
	            g.DrawLine(p, 0, y, w, y);
	            g.DrawString(layer.name, new Font("Sans",8), new SolidBrush(Color.FromKnownColor(KnownColor.Black)), 5, y + 2);
	            g.DrawLine(p, 0, y + LAYER_HEIGHT, w, y + LAYER_HEIGHT);
	        }
	    }
	    
	    /*
	     * ------------------------------------------------------------------------------------------------------
	     * PUBLIC METHODS
	     */
		
		/*
		 * ControlPaint
		 */
        public void ControlPaint (object sender, PaintEventArgs e)
		{
            int i = 0;
            Graphics g = e.Graphics;
            
            RecalcTimelineMiddle();
            
            g.Clear(Color.FromKnownColor(KnownColor.MenuBar));
            DrawTimeLineBackground(g);
            DrawTopLineTime(g);
            
            layer_selected = -1;
            
            g.SetClip(new Rectangle(0, 0, this.Width, h));
            for (i=0; i<_layersDic.Count; i++)
            {
                if (i + this.scrLayers.Value < _layersDic.Count)
                {
                    DrawLayer(g, i, _layersDic[i + this.scrLayers.Value]);
                }
            }
            g.SetClip(new Rectangle(0, 0, this.Width, this.Height));
            
            if (layer_selected == -1)
	        {
                this.ContextMenuStrip = null;
            }
            
            DrawTopLineGuide(g);
            
            g.Flush();
		}
        
        /*
         * ControlMouseDown
         */
        public void ControlMouseDown (object sender, MouseEventArgs e)
        {
            if (e.X > timeline_middle)
            {
            	guide_index = e.X;
            	this.Invalidate();
            }
            else
            {
                guide_index = e.X;
                mouse_y = e.Y;
                this.Invalidate();
            }
        }
		
        /*
         * Context/Layer/Delete
         */
		void DeleteToolStripMenuItemClick(object sender, EventArgs e)
		{
		    if (LayerChange != null)
		    {
		        LayerChange(this, new LayerArgs(layer_selected, LAYER_DELETE));
		    }
		    
		    layer_selected = -1;
		}
		
		/*
		 * Context/Keyframe/Insert
		 */
		void InsertToolStripMenuItemClick(object sender, EventArgs e)
		{
			if (KeyframeChange != null)
			{
				KeyframeChange(this, new KeyframeArgs(layer_selected, guide_index, KEYFRAME_ADD));
			}
		}
		
		/*
		 * Scrollbars change
		 */
		void ScrLayersScroll(object sender, ScrollEventArgs e)
		{
		    this.Invalidate();
		}
	}
	
	/*
     * Arguments
     */
    public class KeyframeArgs
    {
        /*
         * Variables
         */
        public int layer    = 0;
        public int keyframe	= 0;
        public int type		= 0;
        
        /*
         * Constructor
         */
        public KeyframeArgs(int s, int y, int k)
        {
            layer = s;
            keyframe = y;
            type = k;
        }
    }
    
    public class LayerArgs
    {
        /*
         * Variables
         */
        public int layer    = 0;
        public int type     = 0;
        
        /*
         * Constructor
         */
        public LayerArgs(int l, int t)
        {
            layer = l;
            type = t;
        }
    }
    
    /*
     * Keyframe
     */
    public class tlKeyframe
    {
        /*
         * Variables
         */
        public int type = TimeLine.KEYFRAME_SIMPLE_A;
        public int alt = -1;
        
        /*
         * Constructor
         */
        public tlKeyframe()
        {
        }
    }
    
    /*
     * Layer
     */
    public class tlLayer
    {
        /*
         * Variables
         */
        public string name = "";
        public int keyframes_count = 0;
        public List<tlKeyframe> _keyframesDic = new List<tlKeyframe>();
        
        /*
         * Constructor
         */
        public tlLayer(string _name)
        {
            int i=0;
            
            for (i=0; i<TimeLine.MAX_KEYFRAMES; i++)
            {
                _keyframesDic.Add(new tlKeyframe());
                
                if ((i % TimeLine.KEYFRAME_SPACE) == 0)
                {
                    _keyframesDic[i].type = TimeLine.KEYFRAME_SIMPLE_B;
                }
            }
            
            name = _name;
        }
    }
}
